#include "zen_vap_action.h"
#include <vector>
#include <mutex>

namespace Zen { namespace Vap {

	void ActionDispatcher::onUpdate(float interval)
	{
		m_running_duration += interval;

		while(m_actions_running.size())
		{
			auto i = m_actions_running.front();

			if(!i->valid) {
				m_actions_running.pop_front();
				continue;  /// deleted i;
			}

			if(i->next_time > m_running_duration)
				break; /// not the time.

			auto interval_i = m_running_duration - i->time;

			/// in `run()` the stopAction may be called, so keep the action in queue, ...
			auto next = i->action->run((float)interval_i);

			/// ... pop after `action->run`
			m_actions_running.pop_front();

			/**
			 here check valid again, when action->run() the stopAction() maybe be called.
			 */
			if(i->valid && next >= 0) // running
			{
				i->time = m_running_duration;
				// interval should larger than 0, otherwise a dead circle happended.
				i->next_time = m_running_duration + std::max((double)next, 1/120.0);

				pushInRuntimeQueue_(i);
			}
			/// else delete i;
		}

		for(auto i : m_actions_new)
		{
			if(! i->valid) continue;

			auto delay = i->action->start();

			if(delay >= 0 && i->valid)
			{
				i->time = m_running_duration;

				i->next_time = m_running_duration + delay; // here delay can be 0.

				pushInRuntimeQueue_(i);
			}
		}
		m_actions_new.clear();
	}
	void ActionDispatcher::pushInRuntimeQueue_(std::shared_ptr<ActionRuntimeStatus> item)
	{
		for(auto iter = m_actions_running.begin(); iter != m_actions_running.end(); ++iter)
		{
			if((*iter)->next_time >= item->next_time)
			{
				m_actions_running.insert(iter, item);
				return;
			}
		}
		m_actions_running.insert(m_actions_running.end(), item);
	}

	void ActionDispatcher::runAction(std::shared_ptr<Action> action, std::shared_ptr<Node> node)
	{
		if(!action) return;

		auto i = std::make_shared<ActionRuntimeStatus>();
		i->action = action;
		i->node = node;
		m_actions_new.push_back(i);
	}

	void ActionDispatcher::stopAction(std::shared_ptr<Action> action)
	{
		if(!action) return;

		for(auto i : m_actions_running)
		{
			if(i->action == action)
			{
				i->valid = false;
				break;
			}
		}
		for(auto & i : m_actions_new)
		{
			if(i->action == action)
			{
				i->valid = false;
				break;
			}
		}
	}
	void ActionDispatcher::stopAllActions()
	{
		for(auto i : m_actions_new) {
			i->valid = false;
		}
		for(auto i : m_actions_running) {
			i->valid = false;
		}
	}
	void ActionDispatcher::removeActionsAttachedOnNode(std::shared_ptr<Node> node)
	{
		for(auto i : m_actions_running)
		{
			if(i->node.lock() == node)
			{
				i->valid = false;
			}
		}
		for(auto & i : m_actions_new)
		{
			if(i->node.lock() == node)
			{
				i->valid = false;
			}
		}
	}

#define ACTION_STOP (-1.f)

	float ActionCall::run(float delay)
	{
		if(!m_call || !m_call(delay, m_run_times)) return ACTION_STOP;
		++m_run_times;
		if(m_times > 0 && m_run_times >= m_times) return ACTION_STOP;
		return m_interval;
	}
	float ActionCall::start()
	{
		m_run_times = 0;
		return m_delay;
	}

	float ActionTimed::start()
	{
		m_age = 0;
		return m_delay;
	}

	float ActionTimed::run(float interval)
	{
		m_age += interval;

		if(m_lifetime >= 0 && m_age >= m_lifetime)
		{
			m_age = m_lifetime;
			return ACTION_STOP;
		}
		return m_interval;
	}
	
	void PartActionView2D::initTarget(std::shared_ptr<PartOfMatrix2D> target)
	{
		m_target = target;
	}

		// action move

	float ActionMove2D::start() {
		if(m_target.expired()) return ACTION_STOP;

		m_pos_var = { 0, 0 };
		return ActionTimed::start();
	}
	void ActionMove2D::initMove(Vec2 speed, Vec2 acc)
	{
		m_speed = speed;
		m_acc = acc;
	}
	float ActionMove2D::run(float interval)
	{
		if(m_target.expired())
			return false;

		auto age = getAge();
		float x = m_speed.x * age + 0.5f * age * age * m_acc.x;
		float y = m_speed.y * age + 0.5f * age * age * m_acc.y;
		
		auto target = getTarget();
		target->setX(target->getX() + x - m_pos_var.x);
		target->setY(target->getY() + y - m_pos_var.y);

		m_pos_var = { x, y };
		return true;
	}

		// action scale

	void ActionScale2D::initScale(Vec2 speed, Vec2 acc)
	{
		m_speed = speed;
		m_acc = acc;
		m_scale_var = { 0, 0 };
	}

	float ActionScale2D::run(float interval)
	{
		if(m_target.expired())
			return ACTION_STOP;

		auto ret = ActionTimed::run(interval);

		auto age = getAge();
		
		float x = m_speed.x * age + 0.5f * age * age * m_acc.x;
		float y = m_speed.y * age + 0.5f * age * age * m_acc.y;
		
		auto target = getTarget();
		target->setScaleX(target->getScaleX() + x - m_scale_var.x);
		target->setScaleY(target->getScaleY() + y - m_scale_var.y);
		m_scale_var = {x, y};

		return ret;
	}

	float ActionScale2D::start()
	{
		if(m_target.expired()) return ACTION_STOP;
		m_scale_var = { 0, 0 };
		return ActionTimed::start();
	}

		// action rotate

	void ActionRotate2D::initRotate(float velo, float acc)
	{
		m_angular_speed = velo;
		m_acc = acc;
		m_angular_var = 0;
	}
	float ActionRotate2D::run(float interval)
	{
		if(m_target.expired()) return ACTION_STOP;

		auto ret = ActionTimed::run(interval);
		auto age = getAge();
		auto r = m_angular_speed * age + 0.5f * age * age * m_acc;
		auto target = getTarget();
		target->setRotation(target->getRotation() + r - m_angular_var);
		m_angular_var = r;
		return ret;
	}
	float ActionRotate2D::start()
	{
		if(m_target.expired()) return ACTION_STOP;

		m_angular_var = 0;
		return ActionTimed::start();
	}

		// action repeat

	void ActionRepeat::init(std::shared_ptr<Action> action, int times)
	{
		m_action = action;
		m_times = times;
	}

	float ActionRepeat::run(float interval)
	{
		if(!m_action) return ACTION_STOP;

		if(!m_is_inner_run)
		{
			m_is_inner_run = true;
			return m_action->start(); // if start failed, stop repeat.
		}

		auto res = m_action->run(interval);
		if(res >= 0) return res;

		++m_run_times;

		if(m_times > 0 && m_run_times >= m_times)
		{
			return ACTION_STOP;
		}

		m_is_inner_run = false;
		return m_interval;
	}

	float ActionRepeat::start()
	{
		m_run_times = 0;
		m_is_inner_run = false;
		return m_delay;
	}

		// action sequence

	void ActionSequence::addAction(std::shared_ptr<Action> action)
	{
		m_actions.push_back(action);
	}

	float ActionSequence::start()
	{
		if(m_actions.empty()) return ACTION_STOP;
		m_index = 0;
		m_is_inner_run = false;
		return m_delay;
	}
	float ActionSequence::run(float interval)
	{
		if(m_index >= m_actions.size()) return ACTION_STOP;

		auto action = m_actions[m_index];

		if(!m_is_inner_run)
		{
			auto res = action->start();
			if(res >= 0) {
				m_is_inner_run = true;
				return res;
			}
		}
		else
		{
			auto res = action->run(interval);
			if(res >= 0) return res;
			m_is_inner_run = false;
		}
		++m_index;
		return (m_index < m_actions.size() ? m_interval : ACTION_STOP);
	}

	void ActionSequence::initActions(std::vector<std::shared_ptr<Action> > actions)
	{
		m_actions.swap(actions);
	}


	// Create
	std::shared_ptr<Action> ActionCall::
	Create(Function call, int times, float delay, float interval)
	{
		auto act = std::make_shared<ActionCall>();

		act->init(call, times);

		act->setDelay(delay);
		act->setInterval(interval);

		return act;
	}

	void ActionCall::init(Function call, int times)
	{
		this->setCallFunc(call);
		this->setRepeatTimes(times);
	}


	std::shared_ptr<Action> ActionMove2D::
	Create(std::shared_ptr<PartOfMatrix2D> target, float duration,
		   Vec2 speed, Vec2 acc, float delay, float interval)
	{
		auto act = std::make_shared<ActionMove2D>();

		act->initTarget(target);
		act->initMove(speed, acc);

		act->setLifetime(duration);

		act->setDelay(delay);
		act->setInterval(interval);
		return act;
	}
	std::shared_ptr<Action> ActionScale2D::
	Create(std::shared_ptr<PartOfMatrix2D> target, float duration,
		   Vec2 speed, Vec2 acc, float delay, float interval)
	{
		auto act = std::make_shared<ActionScale2D>();
		act->initTarget(target);
		act->setLifetime(duration);
		act->initScale(speed, acc);

		act->setDelay(delay);
		act->setInterval(interval);
		return act;
	}

	std::shared_ptr<Action> ActionRotate2D::
	Create(std::shared_ptr<PartOfMatrix2D> target, float duration,
		   float angular_speed, float acc, float delay, float interval)
	{
		auto act = std::make_shared<ActionRotate2D>();
		act->initTarget(target);
		act->setLifetime(duration);
		act->initRotate(angular_speed, acc);

		act->setDelay(delay);
		act->setInterval(interval);
		return act;
	}

	std::shared_ptr<Action> ActionRepeat::Create(std::shared_ptr<Action> action, int times, float delay, float interval)
	{
		auto act = std::make_shared<ActionRepeat>();
		act->init(action, times);

		act->setDelay(delay);
		act->setInterval(interval);
		return act;
	}
	std::shared_ptr<Action> ActionSequence::Create(std::vector<std::shared_ptr<Action> > actions, float delay, float interval)
	{
		auto act = std::make_shared<ActionSequence>();
		act->initActions(actions);

		act->setDelay(delay);
		act->setInterval(interval);
		return act;
	}
}}
